#if defined (_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
# pragma warning(disable: 4482)
#endif

#ifdef _WIN32
#  include <windows.h>
#  include <io.h>
#else
#  include <unistd.h>
#endif
#include <string>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include <ctype.h>
#include "importer.h"
#include "version.h"

typedef const char *LPCSTR;
typedef char *LPSTR;
#define EOS ('\0')

using namespace google::protobuf;
using namespace std;

///////////////////////////////////////////////////////////////////////////////
const string struct_prefix = "st_";
const string struct_postfix = "";
const char genbypbstru[] = "/* This file is generated by pbstru */\n"
                           "/* version: " VERSION " */\n"
                           "/* DO NOT modify it manually. */\n\n";
///////////////////////////////////////////////////////////////////////////////

const char __THIS_FILE__[] = "pbstru.cpp";
#ifdef _WIN32
const char path_sep[] = "\\";
#else
const char path_sep[] = "/";
#endif

#define BUILD_YEAR_CH0 (__DATE__[7])
#define BUILD_YEAR_CH1 (__DATE__[8])
#define BUILD_YEAR_CH2 (__DATE__[9])
#define BUILD_YEAR_CH3 (__DATE__[10])

#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')


#define BUILD_MONTH_CH0 \
    ((BUILD_MONTH_IS_OCT || BUILD_MONTH_IS_NOV || BUILD_MONTH_IS_DEC) ? '1' : '0')

#define BUILD_MONTH_CH1 \
    ( \
        (BUILD_MONTH_IS_JAN) ? '1' : \
        (BUILD_MONTH_IS_FEB) ? '2' : \
        (BUILD_MONTH_IS_MAR) ? '3' : \
        (BUILD_MONTH_IS_APR) ? '4' : \
        (BUILD_MONTH_IS_MAY) ? '5' : \
        (BUILD_MONTH_IS_JUN) ? '6' : \
        (BUILD_MONTH_IS_JUL) ? '7' : \
        (BUILD_MONTH_IS_AUG) ? '8' : \
        (BUILD_MONTH_IS_SEP) ? '9' : \
        (BUILD_MONTH_IS_OCT) ? '0' : \
        (BUILD_MONTH_IS_NOV) ? '1' : \
        (BUILD_MONTH_IS_DEC) ? '2' : \
        /* error default */    '?' \
    )

#define BUILD_DAY_CH0 ((__DATE__[4] >= '0') ? (__DATE__[4]) : '0')
#define BUILD_DAY_CH1 (__DATE__[5])

#define BUILD_HOUR_CH0 (__TIME__[0])
#define BUILD_HOUR_CH1 (__TIME__[1])

#define BUILD_MIN_CH0 (__TIME__[3])
#define BUILD_MIN_CH1 (__TIME__[4])

#define BUILD_SEC_CH0 (__TIME__[6])
#define BUILD_SEC_CH1 (__TIME__[7])

const char _BUILD_TIME_[] =
{
    BUILD_YEAR_CH0, BUILD_YEAR_CH1, BUILD_YEAR_CH2, BUILD_YEAR_CH3,
    BUILD_MONTH_CH0, BUILD_MONTH_CH1, BUILD_DAY_CH0, BUILD_DAY_CH1,
    BUILD_HOUR_CH0, BUILD_HOUR_CH1, BUILD_MIN_CH0, BUILD_MIN_CH1, BUILD_SEC_CH0, BUILD_SEC_CH1,
    '\0'
};

static string& trim(string& text)
{
    if(!text.empty())
    {
        const char trim_chars[] = " \n\r\t";
        text.erase(0, text.find_first_not_of(trim_chars));
        text.erase(text.find_last_not_of(trim_chars) + 1);
    }
    return text;
}

static void tolower(string& text)
{
    for(string::iterator it=text.begin(); it!=text.end(); ++it)
    {
        *it = tolower(*it);
    }
}

static void toupper(string& text)
{
    for(string::iterator it=text.begin(); it!=text.end(); it++)
    {
        *it = (char) toupper(*it);
    }
}

// Generate public file pbstru_comm.h and pbstru_comm.c
int gen_comm(const string& nf_name, const string &target_dir)
{
    int retcode = 0;
    string filename = target_dir + "include" + path_sep + "pbstru_comm.h";
    FILE *fp = fopen(filename.c_str(), "wt");
    if (NULL == fp)
    {
        printf("Cannot open file:%s for write.\n", filename.c_str());
        return 1;
    }
    fprintf(fp, genbypbstru);
    fprintf(fp, "#pragma once\n");
    fprintf(fp, "\n");
    fprintf(fp, "#define _PBSTRU_BUILD_TIME_ %s\n", _BUILD_TIME_);
    fprintf(fp, "\n");
    fprintf(fp, "#include <stdio.h>\n");
    fprintf(fp, "#include <stdlib.h>\n");
    fprintf(fp, "#include <memory.h>\n");
    fprintf(fp, "\n");
    fprintf(fp, "#define PBSTRU_RC_SUCCESS 0\n");
    fprintf(fp, "#define PBSTRU_RC_BUFOVERFLOW 1\n");
    fprintf(fp, "#define PBSTRU_RC_MAXCOUNT 2\n");
    fprintf(fp, "#define PBSTRU_WRONG_WIRETYPE 3\n");
    fprintf(fp, "\n");
    fprintf(fp, "#define WIRE_TYPE_VARINT 0\n");
    fprintf(fp, "#define WIRE_TYPE_FIX64 1\n");
    fprintf(fp, "#define WIRE_TYPE_LENGTH_DELIMITED 2\n");
    fprintf(fp, "#define WIRE_TYPE_FIX32 5\n");
    fprintf(fp, "\n");
    fprintf(fp, "#ifdef _TEST_CODEC\n");
    fprintf(fp, "#include <stdint.h>\n");
    fprintf(fp, "typedef uint8_t BYTE;\n");
    fprintf(fp, "typedef uint8_t *PBYTE;\n");
    fprintf(fp, "typedef int8_t CHAR;\n");
    fprintf(fp, "typedef uint16_t WORD;\n");
    fprintf(fp, "typedef uint16_t WORD16;\n");
    fprintf(fp, "typedef uint32_t DWORD;\n");
    fprintf(fp, "typedef uint32_t WORD32;\n");
    fprintf(fp, "typedef int32_t SWORD32;\n");
    fprintf(fp, "typedef char *LPSTR;\n");
    fprintf(fp, "typedef const char* LPCSTR;\n");
    fprintf(fp, "typedef uint64_t WORD64;\n");
    fprintf(fp, "typedef int64_t SWORD64;\n");
    fprintf(fp, "\n");
    fprintf(fp, "#ifndef BOOL\n");
    fprintf(fp, "#define BOOL int\n");
    fprintf(fp, "#define TRUE 1\n");
    fprintf(fp, "#define FALSE 0\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "#else\n");
    fprintf(fp, "/* include data type definitions */\n");
    fprintf(fp, "#include \"tulip.h\"\n");
    fprintf(fp, "#include \"tulip_oss.h\"\n");
    fprintf(fp, "\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "#define pbstru_memcpy __builtin_memcpy\n");
    fprintf(fp, "\n");
    fprintf(fp, "/* store utf-8 encoding string */\n");
    fprintf(fp, "typedef struct {\n");
    fprintf(fp, "    char *data;\n");
    fprintf(fp, "    size_t length;\n");
    fprintf(fp, "} ps_string;\n");
    fprintf(fp, "\n");
    fprintf(fp, "/* store binary raw data */\n");
    fprintf(fp, "typedef struct {\n");
    fprintf(fp, "    unsigned char *data;\n");
    fprintf(fp, "    size_t length;\n");
    fprintf(fp, "} ps_bytes;\n");
    fprintf(fp, "\n");

    fprintf(fp, "#ifdef __cplusplus\n");
    fprintf(fp, "extern \"C\" {\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "#define %s_encode_tag_byte(buf, tag, wire_type, offset) %s_encode_tag_byte_%s((buf), (tag), (wire_type), (offset), FALSE)\n", nf_name.c_str(), nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "/* encode tag and wire_type value */\n");
    fprintf(fp, "void %s_encode_tag_byte_%s(BYTE *buf, const BYTE tag, const BYTE wire_type, size_t *offset, BOOL use_old_version);\n", nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "\n");

    fprintf(fp, "#define %s_parse_tag_byte(buf, buflen, field_num, wire_type, offset) %s_parse_tag_byte_%s((buf), (buflen), (field_num), (wire_type), (offset), FALSE, NULL, 0)\n", nf_name.c_str(), nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "/* parse tag and wire_type value */\n");
    fprintf(fp, "BOOL %s_parse_tag_byte_%s(const BYTE* buf, const size_t buflen, WORD *field_num, BYTE *wire_type, size_t *offset, BOOL use_old_version, char *errinfo, const WORD32 maxlen_errinfo);\n", nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "\n");
    fprintf(fp, "#define encode_varint32(value, buf, offset) encode_varint32_%s((value), (buf), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void encode_varint32_%s(const DWORD value, BYTE *buf, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define decode_varint32(buf, buflen, value, offset) decode_varint32_%s((buf), (buflen), (value), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void decode_varint32_%s(const BYTE *buf, const size_t buflen, DWORD *value, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define encode_varint64(value, buf, offset) encode_varint64_%s((value), (buf), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void encode_varint64_%s(const WORD64 value, BYTE *buf, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define decode_varint64(buf, buflen, value, offset) decode_varint64_%s((buf), (buflen), (value), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void decode_varint64_%s(const BYTE *buf, const size_t buflen, WORD64 *value, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define encode_size(value, buf, offset) encode_size_%s((value), (buf), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void encode_size_%s(const size_t value, BYTE *buf, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define decode_size(buf, buflen, value, offset) decode_size_%s((buf), (buflen), (value), (offset))\n", _BUILD_TIME_);
    fprintf(fp, "void decode_size_%s(const BYTE *buf, const size_t buflen, size_t *value, size_t *offset);\n", _BUILD_TIME_);
    fprintf(fp, "#define encode_zigzag32(n) encode_zigzag32_%s((n))\n", _BUILD_TIME_);
    fprintf(fp, "DWORD encode_zigzag32_%s(const SWORD32 n);\n", _BUILD_TIME_);
    fprintf(fp, "#define decode_zigzag32(n) decode_zigzag32_%s((n))\n", _BUILD_TIME_);
    fprintf(fp, "SWORD32 decode_zigzag32_%s(const DWORD n);\n", _BUILD_TIME_);
    fprintf(fp, "#define encode_zigzag64(n) encode_zigzag64_%s((n))\n", _BUILD_TIME_);
    fprintf(fp, "WORD64 encode_zigzag64_%s(const SWORD64 n);\n", _BUILD_TIME_);
    fprintf(fp, "#define decode_zigzag64(n) decode_zigzag64_%s((n))\n", _BUILD_TIME_);
    fprintf(fp, "SWORD64 decode_zigzag64_%s(const WORD64 n);\n", _BUILD_TIME_);
    fprintf(fp, "\n");
    fprintf(fp, "BOOL deal_unknown_field_%s(const BYTE wire_type, const BYTE *buf, const size_t buf_len, size_t *offset, char *errinfo, const WORD32 maxlen_errinfo);\n", _BUILD_TIME_);
    fprintf(fp, "\n");
    fprintf(fp, "#define PRINT_ERRINFO(ret_code) do { \\\n");
    fprintf(fp, "    if(NULL!=errinfo){ \\\n");
    fprintf(fp, "        XOS_snprintf(errinfo, maxlen_errinfo, \"%%d[%%s:%%d]\", (ret_code), __THIS_FILE__, __LINE__); \\\n");
    fprintf(fp, "    } \\\n");
    fprintf(fp, "} while(0)\n");
    fprintf(fp, "\n");
    fprintf(fp, "#ifdef __cplusplus\n");
    fprintf(fp, "}\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "/* end of file */\n");
    fprintf(fp, "\n");
    fclose(fp);

    filename = target_dir + "source" + path_sep + "pbstru_comm.c";
    fp = fopen(filename.c_str(), "wt");
    if (NULL == fp)
    {
        printf("Cannot open file:%s for write.\n", filename.c_str());
        return 1;
    }

    fprintf(fp, genbypbstru);
    fprintf(fp, "#include \"pbstru_comm.h\"\n");
    fprintf(fp, "static const char __THIS_FILE__[] = \"pbstru_comm.c\";\n");
    fprintf(fp, "\n");

    fprintf(fp, "BOOL %s_parse_tag_byte_%s(const BYTE *buf, const size_t buflen, WORD *field_num, BYTE *wire_type, size_t *offset, BOOL old_version, char *errinfo, const WORD32 maxlen_errinfo) {\n", nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "    if(buf[0] & 0x80) {\n");
    fprintf(fp, "        if(buflen<2) {\n");
    fprintf(fp, "            PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\\\n");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        if(TRUE == old_version) {\n");
    fprintf(fp, "            *field_num = (buf[0] & 0x7F) + (buf[1] >> 3) * 128;\n");
    fprintf(fp, "            *wire_type = buf[1] & 0x07;\n");
    fprintf(fp, "        } else {\n");
    fprintf(fp, "            *field_num = (buf[1] << 4) + ((buf[0] & 0x7F) >> 3);\n");
    fprintf(fp, "            *wire_type = buf[0] & 0x07;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += 2;\n");
    fprintf(fp, "    } else {\n");
    fprintf(fp, "        if(buflen<1) {\n");
    fprintf(fp, "            PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\\\n");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *field_num = buf[0] >> 3;\n");
    fprintf(fp, "        *wire_type = buf[0] & 0x07;\n");
    fprintf(fp, "        *offset += 1;\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    return TRUE;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void %s_encode_tag_byte_%s(BYTE *buf, const BYTE tag, const BYTE wire_type, size_t *offset, BOOL old_version) {\n", nf_name.c_str(), _BUILD_TIME_);
    fprintf(fp, "    if(tag < 16) {\n");
    fprintf(fp, "        if (NULL != buf) {\n");
    fprintf(fp, "            buf[*offset] = (tag << 3) | wire_type;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += 1;\n");
    fprintf(fp, "    } else {\n");
    fprintf(fp, "        if (NULL != buf) {\n");
    fprintf(fp, "            if (TRUE == old_version) {\n");
    fprintf(fp, "                buf[*offset] = tag | 0x80;\n");
    fprintf(fp, "                buf[*offset+1] = ((BYTE)(tag/16) & 0x78) | wire_type;\n");
    fprintf(fp, "            } else {\n");
    fprintf(fp, "                buf[*offset] = (tag << 3) | (wire_type & 0x07) | 0x80;\n");
    fprintf(fp, "                buf[*offset+1] = (tag >> 4);\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += 2;\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void encode_varint32_%s(const DWORD value, BYTE *buf, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    DWORD remain_len = value;\n");
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "\n");
    fprintf(fp, "    if (NULL == buf) {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            remain_len = remain_len >> 7;\n");
    fprintf(fp, "            if (0 == remain_len) {\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    } else {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            if ((remain_len >> 7) > 0) {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = ((BYTE)remain_len) | 0x80;\n");
    fprintf(fp, "                remain_len = remain_len >> 7;\n");
    fprintf(fp, "            } else {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = (BYTE)remain_len;\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void decode_varint32_%s(const BYTE *buf, const size_t buflen, DWORD *value, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "    *value = 0;\n");
    fprintf(fp, "    for(iloop=0; iloop<buflen; ++iloop){\n");
    fprintf(fp, "        *value += ((DWORD)(buf[iloop] & 0x7F)) << (7*iloop);\n");
    fprintf(fp, "        if(0 == (buf[iloop] & 0x80)){\n");
    fprintf(fp, "            break;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void encode_varint64_%s(const WORD64 value, BYTE *buf, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    WORD64 remain_len = value;\n");
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "\n");
    fprintf(fp, "    if (NULL == buf) {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            remain_len = remain_len >> 7;\n");
    fprintf(fp, "            if (0 == remain_len) {\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    } else {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            if ((remain_len >> 7) > 0) {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = ((BYTE)remain_len) | 0x80;\n");
    fprintf(fp, "                remain_len = remain_len >> 7;\n");
    fprintf(fp, "            } else {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = (BYTE)remain_len;\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void decode_varint64_%s(const BYTE *buf, const size_t buflen, WORD64 *value, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "    *value = 0;\n");
    fprintf(fp, "    for(iloop=0; iloop<buflen; ++iloop){\n");
    fprintf(fp, "        *value += ((WORD64)(buf[iloop] & 0x7F)) << (7*iloop);\n");
    fprintf(fp, "        if(0 == (buf[iloop] & 0x80)){\n");
    fprintf(fp, "            break;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n\n");

    fprintf(fp, "void encode_size_%s(const size_t value, BYTE *buf, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    size_t remain_len = value;\n");
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "\n");
    fprintf(fp, "    if (NULL == buf) {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            remain_len = remain_len >> 7;\n");
    fprintf(fp, "            if (0 == remain_len) {\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    } else {\n");
    fprintf(fp, "        for (iloop = 0;; ++iloop) {\n");
    fprintf(fp, "            if ((remain_len >> 7) > 0) {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = ((BYTE)remain_len) | 0x80;\n");
    fprintf(fp, "                remain_len = remain_len >> 7;\n");
    fprintf(fp, "            } else {\n");
    fprintf(fp, "                buf[(*offset) + iloop] = (BYTE)remain_len;\n");
    fprintf(fp, "                break;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");

    fprintf(fp, "void decode_size_%s(const BYTE *buf, const size_t buflen, size_t *value, size_t *offset) {\n", _BUILD_TIME_);
    fprintf(fp, "    size_t iloop;\n");
    fprintf(fp, "    *value = 0;\n");
    fprintf(fp, "    size_t max_loop = buflen<4?buflen:4;\n");
    fprintf(fp, "    for(iloop=0; iloop<max_loop; ++iloop){  /* 限制最多4个字节，防止value溢出 */\n");
    fprintf(fp, "        *value += ((size_t)(buf[iloop] & 0x7F)) << (7*iloop);\n");
    fprintf(fp, "        if(0 == (buf[iloop] & 0x80)){\n");
    fprintf(fp, "            break;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    *offset += 1 + iloop;\n");
    fprintf(fp, "}\n\n");

    fprintf(fp, "DWORD encode_zigzag32_%s(const SWORD32 n) {\n", _BUILD_TIME_);
    fprintf(fp, "    return (n>>31)^(n<<1);\n");
    fprintf(fp, "}\n\n");
    fprintf(fp, "SWORD32 decode_zigzag32_%s(const DWORD n) {\n", _BUILD_TIME_);
    fprintf(fp, "    return (n>>1)^(-(n&1));\n");
    fprintf(fp, "}\n\n");
    fprintf(fp, "WORD64 encode_zigzag64_%s(const SWORD64 n) {\n", _BUILD_TIME_);
    fprintf(fp, "    return (n>>63)^(n<<1);\n");
    fprintf(fp, "}\n\n");
    fprintf(fp, "SWORD64 decode_zigzag64_%s(const WORD64 n) {\n", _BUILD_TIME_);
    fprintf(fp, "    return (n>>1)^(-(n&1));\n");
    fprintf(fp, "}\n\n");

    fprintf(fp, "BOOL deal_unknown_field_%s(const BYTE wire_type, const BYTE *buf, const size_t buf_len, size_t *offset, char *errinfo, const WORD32 maxlen_errinfo) {\n", _BUILD_TIME_);
    fprintf(fp, "    size_t tmp_field_len = 0;\n");
    fprintf(fp, "    size_t origin_offset = *offset;\n");
    fprintf(fp, "    \n");
    // fprintf(fp, "    printf(\"wire_type:%%d, buf_len:%%d, offset:%%d\\n\", wire_type, buf_len, *offset);\n");
    fprintf(fp, "    if(buf_len < 1) {\n");
    fprintf(fp, "        return FALSE;\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    \n");
    fprintf(fp, "    switch(wire_type){\n");
    fprintf(fp, "    case WIRE_TYPE_VARINT:\n");
    fprintf(fp, "        decode_size_%s(buf, buf_len, &tmp_field_len, offset);\n", _BUILD_TIME_);
    fprintf(fp, "        break;\n");
    fprintf(fp, "    case WIRE_TYPE_FIX64:\n");
    fprintf(fp, "        if(buf_len < sizeof(WORD64)) {\n");
    fprintf(fp, "            PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += sizeof(WORD64);\n");
    fprintf(fp, "        break;\n");
    fprintf(fp, "    case WIRE_TYPE_LENGTH_DELIMITED:\n");
    fprintf(fp, "        decode_size_%s(buf, buf_len, &tmp_field_len, offset);\n", _BUILD_TIME_);
    fprintf(fp, "        if((buf_len+origin_offset-(*offset)) < tmp_field_len) {\n");
    fprintf(fp, "            PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += tmp_field_len;\n");
    fprintf(fp, "        break;\n");
    fprintf(fp, "    case WIRE_TYPE_FIX32:\n");
    fprintf(fp, "        if(buf_len < sizeof(DWORD)) {\n");
    fprintf(fp, "            PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        *offset += sizeof(DWORD);\n");
    fprintf(fp, "        break;\n");
    fprintf(fp, "    default:\n");
    fprintf(fp, "        break;\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    return TRUE;\n");
    fprintf(fp, "}\n\n");

    fprintf(fp, "/* end of file */\n");
    return retcode;
}

LPSTR proto_filename;
FILE *fp_option = NULL;
// Get the max size of a repeated field.
static bool get_max_count(const string &message_name, const string &field_name, string& max_count) {
    char str1[128];
    char str2[64];
    static string option_filename;
    string key = message_name + "." + field_name;

    if (NULL == fp_option)
    {
        option_filename = proto_filename;
        size_t pos = option_filename.rfind(".");
        if (pos != string::npos)
        {
            option_filename.replace(pos, option_filename.length() - pos, "");
        }
        option_filename += ".options";
        fp_option = fopen(option_filename.c_str(), "rt");
        if (NULL == fp_option)
        {
            printf("Error: [%s:%d] Cannot open file:%s for read.\n", __THIS_FILE__, __LINE__, option_filename.c_str());
            max_count = "10";
            return true;
        }
    }

    for (rewind(fp_option); ;)
    {
        char buffer[1024];
        if (NULL == fgets(buffer, sizeof(buffer), fp_option))
        {
            break;
        }
        if ('#' == buffer[0])
        {
            continue;
        }
        sscanf(buffer, "%127[^ ] %63s", str1, str2);
        if (0 == strcmp(str1, key.c_str()))
        {
            char *num_str = strstr(str2, "max_count:");
            if (NULL == num_str)
            {
                printf("Error: [%s:%d] Cannot read item:\"%s max_count:?\" from option file:[%s].\n",
                    __THIS_FILE__, __LINE__, key.c_str(), option_filename.c_str());
                return false;
            }
            else
            {
                max_count = string(num_str + strlen("max_count:"));
                trim(max_count);
                return true;
            }
        }
    }

    printf("Info: [%s:%d] Cannot read item:\"%s max_count:?\" from option file:[%s], It will be created dynamically.\n", __THIS_FILE__, __LINE__, key.c_str(), option_filename.c_str());
    // exit(NO_MAX_COUNT_IN_FILE);
    return false;
}

void get_struct_list_name(const FieldDescriptor *field, string &struct_list_name)
{
    string field_name_lower = field->name();
    tolower(field_name_lower);
    string field_containing_type_lower = field->containing_type()->name();
    tolower(field_containing_type_lower);
    string msg_enum_name;

    struct_list_name = field_name_lower + "_in_" + field_containing_type_lower;
    switch(field->type())
    {
    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_FIXED32:
        struct_list_name += string("_uint32");
        break;
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_SFIXED32:
        struct_list_name += string("_int32");
        break;

    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_FIXED64:
        struct_list_name += string("_uint64");
        break;
    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_SFIXED64:
        struct_list_name += string("_int64");
        break;

    case FieldDescriptor::TYPE_FLOAT:
        struct_list_name += string("_float");
        break;
    case FieldDescriptor::TYPE_DOUBLE:
        struct_list_name += string("_double");
        break;

    case FieldDescriptor::TYPE_BOOL:
        struct_list_name += string("_boolean");
        break;
    case FieldDescriptor::TYPE_ENUM:
        msg_enum_name = field->enum_type()->name();
        tolower(msg_enum_name);
        struct_list_name += string("_") + msg_enum_name;
        break;

    case FieldDescriptor::TYPE_STRING:
        struct_list_name += string("_string");
        break;
    case FieldDescriptor::TYPE_BYTES:
        struct_list_name += string("_buffer");
        break;
    case FieldDescriptor::TYPE_MESSAGE:
        msg_enum_name = field->message_type()->name();
        tolower(msg_enum_name);
        struct_list_name += string("_") + msg_enum_name;
        break;

    default:
        struct_list_name = "";
        break;
    }
}

static void print_field_in_struct(FILE *fp, const FieldDescriptor *field)
{
    string field_name_upper = field->name();
    toupper(field_name_upper);
    string message_type_upper = field->containing_type()->name();
    toupper(message_type_upper);

    // fprintf(fp, "/* %s\n%s\n%s */\n", field->DebugString().c_str(), field->lowercase_name().c_str(), field->camelcase_name().c_str());
    // const OneofDescriptor* containing_oneof() const;

    if(field->is_optional())
    {
        fprintf(fp, "    BOOL has_%s;\n", field->name().c_str());
    }

    const FieldDescriptor::Type type = field->type();
    switch(type)
    {
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_SFIXED32:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    SWORD32 var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_SFIXED64:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    SWORD64 var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_BOOL:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    BOOL var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_FLOAT:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    float var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_DOUBLE:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    double var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_FIXED32:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    DWORD var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_FIXED64:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    WORD64 var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_STRING:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    ps_string var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_BYTES:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    ps_bytes var_%s;  /* tag:%d */\n", field->name().c_str(), field->number());
        }
        break;

    case FieldDescriptor::TYPE_MESSAGE:
    {
        string message_type_name = field->message_type()->name();
        tolower(message_type_name);
        string struct_name = struct_prefix + message_type_name + struct_postfix;

        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    %s var_%s;  /* tag:%d */\n", struct_name.c_str(), field->name().c_str(), field->number());
        }
    }
    break;
    case FieldDescriptor::TYPE_ENUM:
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "    st_%s_list var_%s;  /* tag:%d */\n",
                    struct_list_name.c_str(), field->name().c_str(), field->number());
        }
        else
        {
            fprintf(fp, "    enum_%s var_%s;  /* tag:%d */\n", field->enum_type()->name().c_str(), field->name().c_str(), field->number());
        }
        break;
    default:
        fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author.\n", __THIS_FILE__, __LINE__, field->type_name());
        break;
    }
}

static int gen_header(const string& nf_name, const Descriptor *desc, const string &target_dir, map<string, string> &map_array_size)
{
    int retcode = 0;
    string name_lower = desc->name();
    tolower(name_lower);
    string struct_name = struct_prefix + name_lower + struct_postfix;

    string filename = target_dir + "include" + path_sep + name_lower;
    filename += ".h";
    FILE *fp = fopen(filename.c_str(), "wt");
    if(NULL == fp)
    {
        printf("Cannot open file:%s for write.\n", filename.c_str());
        return 1;
    }

    fprintf(fp, genbypbstru);
    string name_upper = desc->name();
    toupper(name_upper);
    fprintf(fp, "#ifndef __PBSTRU_%s_H__\n", name_upper.c_str());
    fprintf(fp, "#define __PBSTRU_%s_H__\n", name_upper.c_str());
    fprintf(fp, "\n");
    fprintf(fp, "#ifdef __cplusplus\n");
    fprintf(fp, "extern \"C\"{\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "#include \"pbstru_comm.h\"\n");

    set<string> headers;
    headers.clear();
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(FieldDescriptor::TYPE_MESSAGE == field->type())
        {
            string str = field->message_type()->name();
            tolower(str);
            headers.insert(str);
        }
    }

    // use std::set to delete duplicate header files
    for(set<string>::iterator it=headers.begin(); it!=headers.end(); ++it)
    {
        fprintf(fp, "#include \"%s.h\"\n", it->c_str());
    }
    fprintf(fp, "\n/*\n%s*/\n", desc->DebugString().c_str());

    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated())
        {
            string max_count;
            if(get_max_count(field->containing_type()->full_name(), field->name(), max_count))
            {
                map_array_size[field->containing_type()->name() + ":" + field->name()] = max_count;
            }
        }
    }

    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(FieldDescriptor::TYPE_ENUM == field->type())
        {
            const EnumDescriptor* enum_desc = field->enum_type();
            string enum_name_upper = enum_desc->name();
            toupper(enum_name_upper);
            fprintf(fp, "\n#ifndef ENUM_%s_DEFINED\n", enum_name_upper.c_str());
            fprintf(fp, "#define ENUM_%s_DEFINED\n", enum_name_upper.c_str());
            fprintf(fp, "typedef enum {\n");
            for(int j=0; j<enum_desc->value_count(); ++j)
            {
                fprintf(fp, "    %s_M = %d", enum_desc->value(j)->name().c_str(), enum_desc->value(j)->number());
                // the last enum value has no comma
                if(j!=(enum_desc->value_count()-1))
                {
                    fprintf(fp, ",");
                }
                fprintf(fp, "\n");
            }
            fprintf(fp, "} enum_%s;\n", enum_desc->name().c_str());
            fprintf(fp, "#endif\n");
        }
    }

    // the struct definition of every list
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated())
        {
            string field_name_upper = field->name();
            toupper(field_name_upper);
            string field_containing_type_upper = field->containing_type()->name();
            toupper(field_containing_type_upper);

            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            fprintf(fp, "\ntypedef struct _st_%s_list {\n", struct_list_name.c_str());

            fprintf(fp, "    size_t count;\n");

            switch(field->type())
            {
            case FieldDescriptor::TYPE_UINT32:
            case FieldDescriptor::TYPE_FIXED32:
                fprintf(fp, "    DWORD");
                break;
            case FieldDescriptor::TYPE_INT32:
            case FieldDescriptor::TYPE_SINT32:
            case FieldDescriptor::TYPE_SFIXED32:
                fprintf(fp, "    SWORD32");
                break;
            case FieldDescriptor::TYPE_UINT64:
            case FieldDescriptor::TYPE_FIXED64:
                fprintf(fp, "    WORD64");
                break;
            case FieldDescriptor::TYPE_INT64:
            case FieldDescriptor::TYPE_SINT64:
            case FieldDescriptor::TYPE_SFIXED64:
                fprintf(fp, "    SWORD64");
                break;
            case FieldDescriptor::TYPE_FLOAT:
                fprintf(fp, "    float");
                break;
            case FieldDescriptor::TYPE_DOUBLE:
                fprintf(fp, "    double");
                break;
            case FieldDescriptor::TYPE_BOOL:
                fprintf(fp, "    BOOL");
                break;
            case FieldDescriptor::TYPE_STRING:
                fprintf(fp, "    ps_string");
                break;
            case FieldDescriptor::TYPE_BYTES:
                fprintf(fp, "    ps_bytes");
                break;
            case FieldDescriptor::TYPE_MESSAGE: {
                string message_type_name = field->message_type()->name();
                tolower(message_type_name);
                string struct_name1 = struct_prefix + message_type_name + struct_postfix;
                fprintf(fp, "    %s", struct_name1.c_str());
            }
            break;
            case FieldDescriptor::TYPE_ENUM:
                fprintf(fp, "    enum_%s", field->enum_type()->name().c_str());
                break;
            default:
                fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author for support.\n",
                        __THIS_FILE__, __LINE__, field->type_name());
                break;
            }
            fprintf(fp, " item[%s];  /* tag:%d type:%s */\n",
                    map_array_size[field->containing_type()->name() + ":" + field->name()].c_str(),
                    field->number(), field->type_name());

            fprintf(fp, "} st_%s_list;\n", struct_list_name.c_str());
            fprintf(fp, "#define PBSTRU_MAX_%s_IN_%s %s\n",
                field_name_upper.c_str(), field_containing_type_upper.c_str(),
                map_array_size[field->containing_type()->name() + ":" + field->name()].c_str());
        }
    }

    fprintf(fp, "\ntypedef struct _%s {\n", struct_name.c_str());
    for(int i=0; i<desc->field_count(); ++i)
    {
        print_field_in_struct(fp, desc->field(i));
    }
    fprintf(fp, "\n    size_t _message_length;  // The length of this message, DO NOT set it manually. \n");
    fprintf(fp, "                            // Setting and Using at _internal_encode_message_xxx().\n");
    fprintf(fp, "} %s;\n\n", struct_name.c_str());

    fprintf(fp, "/* construct msg, DO NOT use it high frequency. */\n");
    fprintf(fp, "#define constru_message_%s(msg) constru_message_%s_%s(msg)\n", desc->name().c_str(), desc->name().c_str(), _BUILD_TIME_);
    fprintf(fp, "void constru_message_%s_%s(%s *msg);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());
    fprintf(fp, "/* clear and reuse msg */\n");
    fprintf(fp, "#define clear_message_%s(msg) clear_message_%s_%s(msg)\n", desc->name().c_str(), desc->name().c_str(), _BUILD_TIME_);
    fprintf(fp, "void clear_message_%s_%s(%s *msg);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());
    fprintf(fp, "void _clear_message_%s_len_%s(%s *msg);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());
    fprintf(fp, "#define encode_message_%s(msg, buf, buf_size) encode_message_%s_safe_%s((msg), (buf), (buf_size))\n", desc->name().c_str(), desc->name().c_str(), _BUILD_TIME_);
    fprintf(fp, "size_t encode_message_%s_safe_%s(%s* msg, BYTE* buf, size_t buf_size);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());
    fprintf(fp, "size_t _internal_encode_message_%s_%s(%s* msg, BYTE* buf);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());
    fprintf(fp, "#define decode_message_%s(buf, buf_len, msg) decode_message_%s_%s((buf), (buf_len), (msg), NULL, 0)\n", desc->name().c_str(), desc->name().c_str(), _BUILD_TIME_);
    fprintf(fp, "#define decode_message_%s_ex(buf, buf_len, msg, errinfo, maxlen_errinfo) decode_message_%s_%s((buf), (buf_len), (msg), (errinfo), (maxlen_errinfo))\n", desc->name().c_str(), desc->name().c_str(), _BUILD_TIME_);
    fprintf(fp, "BOOL decode_message_%s_%s(BYTE* buf, const size_t buf_len, %s* msg, char *errinfo, const WORD32 maxlen_errinfo);\n\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str());

    fprintf(fp, "#ifdef __cplusplus\n");
    fprintf(fp, "}\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n");
    fprintf(fp, "#endif\n");
    fprintf(fp, "\n/* end of file */\n");
    fclose(fp);
    return retcode;
}

static void print_clear_message(FILE *fp, const Descriptor *desc, bool init, const map<string,string> &map_array_size)
{
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated() && FieldDescriptor::TYPE_MESSAGE == field->type())
        {
            fprintf(fp, "    size_t i = 0;\n\n");
            break;
        }
    }

    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated())
        {
            string struct_list_name;
            get_struct_list_name(field, struct_list_name);
            if(FieldDescriptor::TYPE_MESSAGE == field->type())
            {
                if(init){
                    string array_name = field->containing_type()->name() + ":" + field->name();
                    fprintf(fp, "\n");
                    fprintf(fp, "    for(i=0; i<%s; ++i) {\n", map_array_size.at(array_name).c_str());
                    fprintf(fp, "        constru_message_%s(&(var_%s->var_%s.item[i]));\n", field->message_type()->name().c_str(), desc->name().c_str(), field->name().c_str());
                    fprintf(fp, "    }\n");
                } else {
                    string array_name = field->containing_type()->name() + ":" + field->name();
                    fprintf(fp, "\n");
                    fprintf(fp, "    if(var_%s->var_%s.count > %s) {\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(array_name).c_str());
                    fprintf(fp, "        var_%s->var_%s.count = %s; /* prevent write overflow */\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(array_name).c_str());
                    fprintf(fp, "    }\n");
                    fprintf(fp, "    for(i=0; i<var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                    fprintf(fp, "        clear_message_%s(&(var_%s->var_%s.item[i]));\n", field->message_type()->name().c_str(), desc->name().c_str(), field->name().c_str());
                    fprintf(fp, "    }\n");
                }
            }
            fprintf(fp, "    var_%s->var_%s.count = 0;\n", desc->name().c_str(), field->name().c_str());
        }
        else
        {
            char spaces[] = "          ";
            if(field->is_optional() && (!init))
            {
                fprintf(fp, "    if(TRUE == var_%s->has_%s) {\n", desc->name().c_str(), field->name().c_str());
                spaces[8] = '\0';
            }
            else
            {
                spaces[4] = '\0';
            }
            switch(field->type())
            {
            case FieldDescriptor::TYPE_FIXED32:
            case FieldDescriptor::TYPE_SFIXED32:
            case FieldDescriptor::TYPE_FIXED64:
            case FieldDescriptor::TYPE_SFIXED64:
            case FieldDescriptor::TYPE_UINT32:
            case FieldDescriptor::TYPE_UINT64:
            case FieldDescriptor::TYPE_INT32:
            case FieldDescriptor::TYPE_INT64:
            case FieldDescriptor::TYPE_SINT32:
            case FieldDescriptor::TYPE_SINT64:
                fprintf(fp, "%svar_%s->var_%s = 0;\n", spaces, desc->name().c_str(), field->name().c_str());
                break;
            case FieldDescriptor::TYPE_FLOAT:
            case FieldDescriptor::TYPE_DOUBLE:
                fprintf(fp, "%svar_%s->var_%s = 0.0;\n", spaces, desc->name().c_str(), field->name().c_str());
                break;
            case FieldDescriptor::TYPE_BOOL:
                fprintf(fp, "%svar_%s->var_%s = FALSE;\n", spaces, desc->name().c_str(), field->name().c_str());
                break;
            case FieldDescriptor::TYPE_ENUM:
                break;
            case FieldDescriptor::TYPE_STRING:
            case FieldDescriptor::TYPE_BYTES:
                fprintf(fp, "%svar_%s->var_%s.length = 0;\n", spaces, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%svar_%s->var_%s.data = NULL;\n", spaces, desc->name().c_str(), field->name().c_str());
                break;
            case FieldDescriptor::TYPE_MESSAGE:
                if(init){
                    fprintf(fp, "%sconstru_message_%s(&(var_%s->var_%s));\n", spaces,
                            field->message_type()->name().c_str(), desc->name().c_str(), field->name().c_str());
                } else {
                    fprintf(fp, "%sclear_message_%s(&(var_%s->var_%s));\n", spaces,
                            field->message_type()->name().c_str(), desc->name().c_str(), field->name().c_str());
                }
                break;
            default:
                fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author.\n", __THIS_FILE__, __LINE__, field->type_name());
                break;
            }
            if(field->is_optional() && (!init))
            {
                fprintf(fp, "    }\n");
            }

            if(field->is_optional())
            {
                fprintf(fp, "    var_%s->has_%s = FALSE;\n", desc->name().c_str(), field->name().c_str());
            }
        }
    }
}

static void print_clear_message_len(FILE *fp, const Descriptor *desc, const map<string, string> &map_array_size)
{
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(FieldDescriptor::TYPE_MESSAGE == field->type())
        {
            if(field->is_repeated())
            {
                fprintf(fp, "    size_t i;\n\n");
                break;
            }
        }
    }
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(FieldDescriptor::TYPE_MESSAGE == field->type())
        {
            if(field->is_repeated())
            {
                string array_name = field->containing_type()->name() + ":" + field->name();
                fprintf(fp, "\n");
                fprintf(fp, "    if(var_%s->var_%s.count > %s) {\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(array_name).c_str());
                fprintf(fp, "        var_%s->var_%s.count = %s; /* prevent write overflow */\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(array_name).c_str());
                fprintf(fp, "    }\n");

                fprintf(fp, "    for(i=0; i<var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "        _clear_message_%s_len_%s(&(var_%s->var_%s.item[i]));\n", field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "    }\n");
            } else {
                fprintf(fp, "    _clear_message_%s_len_%s(&(var_%s->var_%s));\n", field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
        }
    }
    fprintf(fp, "    var_%s->_message_length = 0;\n", desc->name().c_str());
}

static int gen_source(const string& nf_name, const Descriptor *desc, const string &target_dir, const map<string,string> &map_array_size, bool use_old_version)
{
    int retcode = 0;
    string name_lower = desc->name();
    tolower(name_lower);
    string struct_name = struct_prefix + name_lower + struct_postfix;

    string basefile = name_lower + ".c";
    string filename = target_dir + "source" + path_sep + basefile;
    FILE *fp = fopen(filename.c_str(), "wt");
    if(NULL == fp)
    {
        printf("Cannot open file:%s for write.\n", filename.c_str());
        return 1;
    }

    fprintf(fp, genbypbstru);
    fprintf(fp, "#include \"%s.h\"\n", name_lower.c_str());
    fprintf(fp, "static const char __THIS_FILE__[] = \"%s\";\n", basefile.c_str());
    fprintf(fp, "\n");
    fprintf(fp, "/* lint -save -e701 -e647 */\n");
    fprintf(fp, "\n");

    ////////////////////////////////////////
    // clear function
    fprintf(fp, "void constru_message_%s_%s(%s* var_%s) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());
    print_clear_message(fp, desc, true, map_array_size);
    fprintf(fp, "}\n\n");

    fprintf(fp, "void clear_message_%s_%s(%s* var_%s) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());
    print_clear_message(fp, desc, false, map_array_size);
    fprintf(fp, "}\n\n");

    fprintf(fp, "void _clear_message_%s_len_%s(%s* var_%s) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());
    print_clear_message_len(fp, desc, map_array_size);
    fprintf(fp, "}\n\n");

    ////////////////////////////////////////
    fprintf(fp, "size_t _internal_encode_message_%s_%s(%s* var_%s, BYTE* buf) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());

    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated())
        {
            fprintf(fp, "    size_t i = 0;\n");
            break;
        }
    }
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        if(field->is_repeated() && field->is_packed())
        {
            fprintf(fp, "    size_t packed_bytes_size = 0;\n");
            fprintf(fp, "    BYTE *packed_bytes = NULL;\n");
            break;
        }
    }
    fprintf(fp, "    size_t offset = 0;\n\n");

    // encode every fields
    for(int i=0; i<desc->field_count(); ++i)
    {
        string prefix_spaces = "    ";
        const FieldDescriptor *field = desc->field(i);

        if(field->is_repeated())
        {
            if(field->is_packed())
            {
                fprintf(fp, "    if(var_%s->var_%s.count>0) {\n", desc->name().c_str(), field->name().c_str());
                switch(field->type())
                {
                case FieldDescriptor::TYPE_FIXED32:
                case FieldDescriptor::TYPE_SFIXED32:
                case FieldDescriptor::TYPE_FLOAT:
                    fprintf(fp, "        %s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                    fprintf(fp, "        encode_size_%s(((var_%s->var_%s.count)<<2), buf, &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    break;
                case FieldDescriptor::TYPE_FIXED64:
                case FieldDescriptor::TYPE_SFIXED64:
                case FieldDescriptor::TYPE_DOUBLE:
                    fprintf(fp, "        %s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                    fprintf(fp, "        encode_size_%s(((var_%s->var_%s.count)<<3), buf, &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    break;
                case FieldDescriptor::TYPE_BOOL:
                case FieldDescriptor::TYPE_UINT64:
                case FieldDescriptor::TYPE_INT64:
                case FieldDescriptor::TYPE_SINT64:
                case FieldDescriptor::TYPE_ENUM:
                    fprintf(fp, "        %s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                    fprintf(fp, "        packed_bytes_size = 0;\n");
                    fprintf(fp, "        for(i = 0; i < var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                    if(FieldDescriptor::TYPE_SINT64==field->type()){
                        fprintf(fp, "            encode_varint64_%s(encode_zigzag64_%s(var_%s->var_%s.item[i]), packed_bytes, &packed_bytes_size);\n", _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    } else {
                        fprintf(fp, "            encode_varint64_%s(var_%s->var_%s.item[i], packed_bytes, &packed_bytes_size);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    }
                    fprintf(fp, "        }\n");
                    fprintf(fp, "        encode_size_%s(packed_bytes_size, buf, &offset);\n", _BUILD_TIME_);
                    break;
                case FieldDescriptor::TYPE_UINT32:
                case FieldDescriptor::TYPE_INT32:
                case FieldDescriptor::TYPE_SINT32:
                    fprintf(fp, "        %s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                    fprintf(fp, "        packed_bytes_size = 0;\n");
                    fprintf(fp, "        for(i = 0; i < var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                    if(FieldDescriptor::TYPE_SINT32==field->type()){
                        fprintf(fp, "            encode_varint32_%s(encode_zigzag32_%s(var_%s->var_%s.item[i]), packed_bytes, &packed_bytes_size);\n", _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    } else {
                        fprintf(fp, "            encode_varint32_%s(var_%s->var_%s.item[i], packed_bytes, &packed_bytes_size);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                    }
                    fprintf(fp, "        }\n");
                    fprintf(fp, "        encode_size_%s(packed_bytes_size, buf, &offset);\n", _BUILD_TIME_);
                    break;
                default:
                    fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author.\n", __THIS_FILE__, __LINE__, field->type_name());
                    break;
                }
                fprintf(fp, "        for(i = 0; i < var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                prefix_spaces = "            ";
            } else {
                fprintf(fp, "    for(i = 0; i < var_%s->var_%s.count; ++i) {\n", desc->name().c_str(), field->name().c_str());
                prefix_spaces = "        ";
            }
        }
        else if(desc->field(i)->is_optional())
        {
            fprintf(fp, "    if(var_%s->has_%s) {\n", desc->name().c_str(), field->name().c_str());
            prefix_spaces = "        ";
        }
        else
        {
            prefix_spaces = "    ";
        }

        fprintf(fp, "%s/* tag:%d type:%s */\n", prefix_spaces.c_str(), field->number(), field->type_name());
        switch(field->type())
        {
        case FieldDescriptor::TYPE_FIXED32:
        case FieldDescriptor::TYPE_SFIXED32:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_FIX32, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }
            fprintf(fp, "%sif(NULL != buf) {\n", prefix_spaces.c_str());
            if(field->is_repeated())
            {
                fprintf(fp, "%s    *((DWORD *)(buf + offset)) = var_%s->var_%s.item[i];\n",
                        prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s    *((DWORD *)(buf + offset)) = var_%s->var_%s;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "%s}\n", prefix_spaces.c_str());
            fprintf(fp, "%soffset += sizeof(DWORD);\n", prefix_spaces.c_str());
            break;

        case FieldDescriptor::TYPE_FIXED64:
        case FieldDescriptor::TYPE_SFIXED64:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_FIX64, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }
            fprintf(fp, "%sif(NULL != buf) {\n", prefix_spaces.c_str());
            if(field->is_repeated())
            {
                fprintf(fp, "%s    *((WORD64 *)(buf + offset)) = var_%s->var_%s.item[i];\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s    *((WORD64 *)(buf + offset)) = var_%s->var_%s;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "%s}\n", prefix_spaces.c_str());
            fprintf(fp, "%soffset += sizeof(WORD64);\n", prefix_spaces.c_str());
            break;

        case FieldDescriptor::TYPE_FLOAT:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_FIX32, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }
            fprintf(fp, "%sif(NULL != buf) {\n", prefix_spaces.c_str());
            if(field->is_repeated())
            {
                fprintf(fp, "%s    *((float *)(buf + offset)) = var_%s->var_%s.item[i];\n",
                        prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s    *((float *)(buf + offset)) = var_%s->var_%s;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "%s}\n", prefix_spaces.c_str());
            fprintf(fp, "%soffset += sizeof(float);\n", prefix_spaces.c_str());
            break;

        case FieldDescriptor::TYPE_DOUBLE:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_FIX64, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }
            fprintf(fp, "%sif(NULL != buf) {\n", prefix_spaces.c_str());
            if(field->is_repeated())
            {
                fprintf(fp, "%s    *((double *)(buf + offset)) = var_%s->var_%s.item[i];\n",
                        prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s    *((double *)(buf + offset)) = var_%s->var_%s;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "%s}\n", prefix_spaces.c_str());
            fprintf(fp, "%soffset += sizeof(double);\n", prefix_spaces.c_str());
            break;

        case FieldDescriptor::TYPE_BOOL:
        case FieldDescriptor::TYPE_ENUM:
        case FieldDescriptor::TYPE_UINT64:
        case FieldDescriptor::TYPE_INT64:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_VARINT, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }

            if(field->is_repeated())
            {
                fprintf(fp, "%sencode_varint64_%s(var_%s->var_%s.item[i], buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%sencode_varint64_%s(var_%s->var_%s, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_SINT64:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_VARINT, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }

            if(field->is_repeated())
            {
                fprintf(fp, "%sencode_varint64_%s(encode_zigzag64_%s(var_%s->var_%s.item[i]), buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%sencode_varint64_%s(encode_zigzag64_%s(var_%s->var_%s), buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_UINT32:
        case FieldDescriptor::TYPE_INT32:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_VARINT, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }

            if(field->is_repeated())
            {
                fprintf(fp, "%sencode_varint32_%s(var_%s->var_%s.item[i], buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%sencode_varint32_%s(var_%s->var_%s, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_SINT32:
            if(!field->is_packed())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_VARINT, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            }

            if(field->is_repeated())
            {
                fprintf(fp, "%sencode_varint32_%s(encode_zigzag32_%s(var_%s->var_%s.item[i]), buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%sencode_varint32_%s(encode_zigzag32_%s(var_%s->var_%s), buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_STRING:
            if(field->is_repeated())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s.item[i].length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%sif (NULL != buf && var_%s->var_%s.item[i].length > 0) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    pbstru_memcpy(buf + offset, (unsigned char *)var_%s->var_%s.item[i].data, var_%s->var_%s.item[i].length);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%soffset += var_%s->var_%s.item[i].length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s.length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%sif (NULL != buf && var_%s->var_%s.length > 0) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    pbstru_memcpy(buf + offset, (unsigned char *)var_%s->var_%s.data, var_%s->var_%s.length);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%soffset += var_%s->var_%s.length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_BYTES:
            if(field->is_repeated())
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s.item[i].length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%sif (NULL != buf && var_%s->var_%s.item[i].length > 0) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    pbstru_memcpy(buf + offset, var_%s->var_%s.item[i].data, var_%s->var_%s.item[i].length);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%soffset += var_%s->var_%s.item[i].length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s.length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%sif (NULL != buf && var_%s->var_%s.length > 0) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    pbstru_memcpy(buf + offset, var_%s->var_%s.data, var_%s->var_%s.length);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%soffset += var_%s->var_%s.length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
            }
            break;

        case FieldDescriptor::TYPE_MESSAGE:
            fprintf(fp, "%s%s_encode_tag_byte_%s(buf, %d, WIRE_TYPE_LENGTH_DELIMITED, &offset, %s);\n", prefix_spaces.c_str(), nf_name.c_str(), _BUILD_TIME_, field->number(), use_old_version?"TRUE":"FALSE");
            if(field->is_repeated())
            {
                fprintf(fp, "%sif (0 == var_%s->var_%s.item[i]._message_length) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    var_%s->var_%s.item[i]._message_length = _internal_encode_message_%s_%s(&(var_%s->var_%s.item[i]), NULL);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s.item[i]._message_length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "%sif (0 == var_%s->var_%s._message_length) {\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s    var_%s->var_%s._message_length = _internal_encode_message_%s_%s(&(var_%s->var_%s), NULL);\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str(), field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
                fprintf(fp, "%sencode_size_%s(var_%s->var_%s._message_length, buf, &offset);\n", prefix_spaces.c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
            }

            if(field->is_repeated())
            {
                fprintf(fp, "%sif(NULL == buf) {\n", prefix_spaces.c_str());
                fprintf(fp, "%s    offset += var_%s->var_%s.item[i]._message_length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s} else {\n", prefix_spaces.c_str());
                fprintf(fp, "%s    offset += _internal_encode_message_%s_%s(&(var_%s->var_%s.item[i]), buf + offset);\n", prefix_spaces.c_str(), field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
            }
            else
            {
                fprintf(fp, "%sif(NULL == buf) {\n", prefix_spaces.c_str());
                fprintf(fp, "%s    offset += var_%s->var_%s._message_length;\n", prefix_spaces.c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s} else {\n", prefix_spaces.c_str());
                fprintf(fp, "%s    offset += _internal_encode_message_%s_%s(&(var_%s->var_%s), buf + offset);\n", prefix_spaces.c_str(), field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s}\n", prefix_spaces.c_str());
            }
            break;

        default:
            fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author.\n", __THIS_FILE__, __LINE__, field->type_name());
            break;
        }
        if(desc->field(i)->is_repeated() && desc->field(i)->is_packed())
        {
            fprintf(fp, "        }\n");
        }
        if(desc->field(i)->is_repeated() || desc->field(i)->is_optional())
        {
            fprintf(fp, "    }\n");
        }
        // fprintf(fp, "printf(\"[%%s:%%d] offset:%%u\\n\", __FILE__, __LINE__, offset);\n");
        fprintf(fp, "\n");
    }
    fprintf(fp, "    return offset;\n");
    fprintf(fp, "}\n\n");

    ////////////////////////////////////////
    fprintf(fp, "size_t encode_message_%s_safe_%s(%s* var_%s, BYTE* buf, size_t buf_size) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());
    fprintf(fp, "    _clear_message_%s_len_%s(var_%s); \n\n", desc->name().c_str(), _BUILD_TIME_, desc->name().c_str());
    fprintf(fp, "    if (_internal_encode_message_%s_%s(var_%s, NULL) > buf_size) {\n", desc->name().c_str(), _BUILD_TIME_, desc->name().c_str());
    fprintf(fp, "        return 0;\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    return _internal_encode_message_%s_%s(var_%s, buf);\n", desc->name().c_str(), _BUILD_TIME_, desc->name().c_str());
    fprintf(fp, "}\n\n");

///////////////////////////////////////////////////////////////////////////
// Decode function
    fprintf(fp, "\nBOOL decode_message_%s_%s(BYTE* buf, const size_t buf_len, %s* var_%s, char *errinfo, const WORD32 maxlen_errinfo) {\n", desc->name().c_str(), _BUILD_TIME_, struct_name.c_str(), desc->name().c_str());
    fprintf(fp, "    size_t offset = 0;\n");
    // use this variable if has a sub-message
    for(int i=0; i<desc->field_count(); ++i)
    {
        if(FieldDescriptor::TYPE_MESSAGE == desc->field(i)->type())
        {
            fprintf(fp, "    size_t tmp_message_len = 0;\n");
            break;
        }
    }
    fprintf(fp, "    WORD field_num;\n");
    fprintf(fp, "    BYTE wire_type;\n\n");
    fprintf(fp, "    clear_message_%s(var_%s);\n", desc->name().c_str(), desc->name().c_str());
    fprintf(fp, "    if(0 == buf_len) {\n");
    fprintf(fp, "        PRINT_ERRINFO(PBSTRU_RC_SUCCESS);\n");
    fprintf(fp, "        return TRUE;\n");
    fprintf(fp, "    }\n\n");

    fprintf(fp, "    for(;offset < buf_len;) {\n");
    fprintf(fp, "        if(FALSE == %s_parse_tag_byte_%s(buf+offset, buf_len-offset, &field_num, &wire_type, &offset, %s, errinfo, maxlen_errinfo)) {\n", nf_name.c_str(), _BUILD_TIME_, use_old_version?"TRUE":"FALSE");
    fprintf(fp, "            return FALSE;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "        switch(field_num) {\n");
    for(int i=0; i<desc->field_count(); ++i)
    {
        const FieldDescriptor *field = desc->field(i);
        fprintf(fp, "        case %d:  /* type:%s */\n", field->number(), field->type_name());
        switch(field->type())
        {
        case FieldDescriptor::TYPE_FIXED32:
        case FieldDescriptor::TYPE_SFIXED32:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_FIX32 == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                if((offset + sizeof(DWORD)) > buf_len) {\n", spaces);
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n", spaces);
                fprintf(fp, "%s                    return FALSE;\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = *((DWORD *)(buf + offset));\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                offset += sizeof(DWORD);\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                if((offset + sizeof(DWORD)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((DWORD *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(DWORD);\n");
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                if((offset + sizeof(DWORD)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((DWORD *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(DWORD);\n");
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_FLOAT:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_FIX32 == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                if((offset + sizeof(float)) > buf_len) {\n", spaces);
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n", spaces);
                fprintf(fp, "%s                    return FALSE;\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = *((float *)(buf + offset));\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                offset += sizeof(float);\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                if((offset + sizeof(float)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((float *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(float);\n");
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                if((offset + sizeof(float)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((float *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(float);\n");
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_FIXED64:
        case FieldDescriptor::TYPE_SFIXED64:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_FIX64 == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                if((offset + sizeof(WORD64)) > buf_len) {\n", spaces);
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n", spaces);
                fprintf(fp, "%s                    return FALSE;\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = *((WORD64 *)(buf + offset));\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                offset += sizeof(WORD64);\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                if((offset + sizeof(WORD64)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((WORD64 *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(WORD64);\n");
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                if((offset + sizeof(WORD64)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((WORD64 *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(WORD64);\n");
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_DOUBLE:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_FIX64 == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                if((offset + sizeof(double)) > buf_len) {\n", spaces);
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n", spaces);
                fprintf(fp, "%s                    return FALSE;\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = *((double *)(buf + offset));\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                offset += sizeof(double);\n", spaces);
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                if((offset + sizeof(double)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((double *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(double);\n");
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                if((offset + sizeof(double)) > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s = *((double *)(buf + offset));\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += sizeof(double);\n");
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_BOOL:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_VARINT == wire_type) {\n");
            }
                fprintf(fp, "                WORD64 tmp_value;\n");
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", spaces, _BUILD_TIME_);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = (tmp_value==0)?FALSE:TRUE;\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed()) {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                var_%s->var_%s = (tmp_value==0)?FALSE:TRUE;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                var_%s->var_%s = (tmp_value==0)?FALSE:TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_ENUM:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_VARINT == wire_type) {\n");
            }
                fprintf(fp, "                WORD64 tmp_value;\n");
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", spaces, _BUILD_TIME_);
                fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = (int)tmp_value;\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed()) {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                var_%s->var_%s = (int)tmp_value;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, &tmp_value, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                var_%s->var_%s = (int)tmp_value;\n", desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_UINT64:
        case FieldDescriptor::TYPE_INT64:
        case FieldDescriptor::TYPE_SINT64:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_VARINT == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                decode_varint64_%s(buf+offset, buf_len-offset, (WORD64 *)&(var_%s->var_%s.item[var_%s->var_%s.count]), &offset);\n", spaces, _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT64==field->type()) {
                    fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = decode_zigzag64_%s(var_%s->var_%s.item[var_%s->var_%s.count]);\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                }
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, (WORD64 *)&(var_%s->var_%s), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT64 == field->type()) {
                    fprintf(fp, "                var_%s->var_%s = decode_zigzag64_%s(var_%s->var_%s);\n", desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                }
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_varint64_%s(buf+offset, buf_len-offset, (WORD64 *)&(var_%s->var_%s), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT64 == field->type()) {
                    fprintf(fp, "                var_%s->var_%s = decode_zigzag64_%s(var_%s->var_%s);\n", desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                }
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_UINT32:
        case FieldDescriptor::TYPE_INT32:
        case FieldDescriptor::TYPE_SINT32:
            if(field->is_packed()) {
                fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            } else {
                fprintf(fp, "            if(WIRE_TYPE_VARINT == wire_type) {\n");
            }
            if(field->is_repeated())
            {
                char spaces[100];
                spaces[0] = '\0';
                if(field->is_packed())
                {
                    fprintf(fp, "                size_t array_size = 0;  /* packed repeated field */\n");
                    fprintf(fp, "                size_t data_offset;\n");
                    fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &array_size, &offset);\n", _BUILD_TIME_);
                    fprintf(fp, "                for(data_offset=offset; (offset-data_offset)<array_size; ) {\n");
                    strcpy(spaces, "    ");
                }
                fprintf(fp, "%s                if(var_%s->var_%s.count >= %s) {\n", spaces, desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "%s                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n", spaces);
                fprintf(fp, "%s                    return FALSE;  /* out of range */\n", spaces);
                fprintf(fp, "%s                }\n", spaces);
                fprintf(fp, "%s                decode_varint32_%s(buf+offset, buf_len-offset, (DWORD *)&(var_%s->var_%s.item[var_%s->var_%s.count]), &offset);\n", spaces, _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT32 == field->type()){
                    fprintf(fp, "%s                var_%s->var_%s.item[var_%s->var_%s.count] = decode_zigzag32_%s(var_%s->var_%s.item[var_%s->var_%s.count]);\n", spaces, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                }
                fprintf(fp, "%s                var_%s->var_%s.count += 1;\n", spaces, desc->name().c_str(), field->name().c_str());
                if(field->is_packed())
                {
                    fprintf(fp, "                }\n");
                }
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_varint32_%s(buf+offset, buf_len-offset, (DWORD *)&(var_%s->var_%s), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT32==field->type()){
                    fprintf(fp, "                var_%s->var_%s = decode_zigzag32_%s(var_%s->var_%s);\n", desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                }
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_varint32_%s(buf+offset, buf_len-offset, (DWORD *)&(var_%s->var_%s), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                if(FieldDescriptor::TYPE_SINT32==field->type()){
                    fprintf(fp, "                var_%s->var_%s = decode_zigzag32_%s(var_%s->var_%s);\n", desc->name().c_str(), field->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                }
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_STRING:
            fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            if(field->is_repeated())
            {
                fprintf(fp, "                if(var_%s->var_%s.count >= %s) {\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n");
                fprintf(fp, "                    return FALSE;  /* out of range */\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.item[var_%s->var_%s.count].length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.item[var_%s->var_%s.count].length) > buf_len) {\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.item[var_%s->var_%s.count].data = (char *)(buf + offset);\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.item[var_%s->var_%s.count].length;\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->var_%s.count += 1;\n", desc->name().c_str(), field->name().c_str());
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.length) > buf_len) {\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.data = (char *)(buf + offset);\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.length;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.length) > buf_len) {\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.data = (char *)(buf + offset);\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.length;\n", desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_BYTES:
            fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            if(field->is_repeated())
            {
                fprintf(fp, "                if(var_%s->var_%s.count >= %s) {\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n");
                fprintf(fp, "                    return FALSE;  /* out of range */\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.item[var_%s->var_%s.count].length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.item[var_%s->var_%s.count].length) > buf_len) {\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.item[var_%s->var_%s.count].data = buf + offset;\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.item[var_%s->var_%s.count].length;\n", desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->var_%s.count += 1;\n", desc->name().c_str(), field->name().c_str());
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.length) > buf_len) {\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.data = buf + offset;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.length;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &(var_%s->var_%s.length), &offset);\n", _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                if((offset + var_%s->var_%s.length) > buf_len) {\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                var_%s->var_%s.data = buf + offset;\n", desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                offset += var_%s->var_%s.length;\n", desc->name().c_str(), field->name().c_str());
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;

        case FieldDescriptor::TYPE_MESSAGE:
            fprintf(fp, "            if(WIRE_TYPE_LENGTH_DELIMITED == wire_type) {\n");
            if(field->is_repeated())
            {
                fprintf(fp, "                if(var_%s->var_%s.count >= %s) {\n", desc->name().c_str(), field->name().c_str(), map_array_size.at(field->containing_type()->name() + ":" + field->name()).c_str());
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_MAXCOUNT);\n");
                fprintf(fp, "                    return FALSE;  /* out of range */\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &tmp_message_len, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                if(offset + tmp_message_len > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                if(FALSE == decode_message_%s_%s(buf + offset, tmp_message_len, &(var_%s->var_%s.item[var_%s->var_%s.count]), errinfo, maxlen_errinfo)) {\n", field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str(), desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                offset += tmp_message_len;\n");
                fprintf(fp, "                var_%s->var_%s.count += 1;\n", desc->name().c_str(), field->name().c_str());
            }
            else if(field->is_optional())
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &tmp_message_len, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                if(offset + tmp_message_len > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                if(FALSE == decode_message_%s_%s(buf + offset, tmp_message_len, &(var_%s->var_%s), errinfo, maxlen_errinfo)) {\n", field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                offset += tmp_message_len;\n");
                fprintf(fp, "                var_%s->has_%s = TRUE;\n", desc->name().c_str(), field->name().c_str());
            }
            else
            {
                fprintf(fp, "                decode_size_%s(buf+offset, buf_len-offset, &tmp_message_len, &offset);\n", _BUILD_TIME_);
                fprintf(fp, "                if(offset + tmp_message_len > buf_len) {\n");
                fprintf(fp, "                    PRINT_ERRINFO(PBSTRU_RC_BUFOVERFLOW);\n");
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                if(FALSE == decode_message_%s_%s(buf + offset, tmp_message_len, &(var_%s->var_%s), errinfo, maxlen_errinfo)) {\n", field->message_type()->name().c_str(), _BUILD_TIME_, desc->name().c_str(), field->name().c_str());
                fprintf(fp, "                    return FALSE;\n");
                fprintf(fp, "                }\n");
                fprintf(fp, "                offset += tmp_message_len;\n");
            }
            fprintf(fp, "            } else {\n");
            fprintf(fp, "                PRINT_ERRINFO(PBSTRU_WRONG_WIRETYPE);\n");
            fprintf(fp, "                return FALSE;\n");
            fprintf(fp, "            }\n");
            break;
        default:
            fprintf(fp, "[%s:%d] Unknown field type:%s, Please contact the author.\n", __THIS_FILE__, __LINE__, field->type_name());
            break;
        }
        fprintf(fp, "            break;\n\n");
    }
    fprintf(fp, "        default:\n");
    fprintf(fp, "            if(FALSE == deal_unknown_field_%s(wire_type, buf+offset, buf_len-offset, &offset, errinfo, maxlen_errinfo)){\n", _BUILD_TIME_);
    fprintf(fp, "                return FALSE;\n");
    fprintf(fp, "            }\n");
    fprintf(fp, "            break;\n");
    fprintf(fp, "        }\n");
    fprintf(fp, "    }\n");
    fprintf(fp, "    return TRUE;\n");
    fprintf(fp, "}\n");
    fprintf(fp, "\n");
    fprintf(fp, "/* lint -restore */\n");
    fprintf(fp, "/* end of file */\n");
    fclose(fp);
    return retcode;
}

int gen_all_from_file(const string& nf_name, const FileDescriptor *f, const string &target_dir)
{
    int retcode = 0;
    for(int i=0; i<f->message_type_count(); ++i)
    {
        bool use_old_version = false;
        const Descriptor* desc = f->message_type(i);
        if(desc->name()=="GLOBAL_T" || desc->name()=="codec_cdb_GLOBAL_T"){
            // Backward compatibility is required for messages.
            printf("Info: message type is %s, use old version to encode tag info.\n", desc->name().c_str());
            use_old_version = true;
        } else {
            int largest_tag = 0;
            // Get largest tag value
            for(int j=0; j<desc->field_count(); ++j)
            {
                const FieldDescriptor *field = desc->field(j);
                if(field->number() > largest_tag) {
                    largest_tag = field->number();
                }
            }
            // 第三方定义的proto，基本上都是syntax3
            if(f->syntax() > 2 || desc->name()=="ut_test_message" || desc->name()=="App2HttpReq" || desc->name()=="Http2AppRes") {
                // tag must less than 256
                if(largest_tag > 255) {
                    printf("error -- message:'%s' largest tag:%d is great than 255.\n", desc->name().c_str(), largest_tag);
                    return -2;
                }
            } else {
                // tag must less than 16
                if(largest_tag > 15) {
                    printf("error -- message:'%s' largest tag:%d is great than 15.\n", desc->name().c_str(), largest_tag);
                    return -3;
                }
            }
        }

        map<string, string> map_array_size;
        retcode = gen_header(nf_name, desc, target_dir, map_array_size);
        if(0 == retcode)
        {
            retcode = gen_source(nf_name, desc, target_dir, map_array_size, use_old_version);
        }
        map_array_size.clear();

        /* close option file */
        if(NULL != fp_option)
        {
            fclose(fp_option);
            fp_option = NULL;
        }
    }
    return retcode;
}

class ImporterError : public compiler::MultiFileErrorCollector
{
private:
    void AddError(
        const string & filename,
        int line,
        int column,
        const string & message)
    {
        printf("%s %d:%d -- %s", filename.c_str(), line, column, message.c_str());
    }
};

/* RAII */
static inline void freep(char **p)
{
    if(*p)
    {
        free(*p);
    }
    *p = NULL;
}

int create_path(string &path)
{
    if (path[path.length() - 1] != path_sep[0])
    {
        path += path_sep;
    }
#ifdef _WIN32
    return ::CreateDirectory(path.c_str(), NULL);
#else
    char dir_name[512];
    strcpy(dir_name, path.c_str());

    size_t len = path.length();
    for(size_t i=1; i<len; i++)
    {
        if(dir_name[i] == path_sep[0])
        {
            dir_name[i] = EOS;
            if(access(dir_name, 0))
            {
#ifdef _WIN32
                if(mkdir(dir_name))
#else
                if(mkdir(dir_name, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
#endif
                {
                    printf("Cannot create directory: %s\n", dir_name);
                    return -1;
                }
            }
            dir_name[i] = path_sep[0];
        }
    }
    return 0;
#endif
}

int get_syntax(LPCSTR proto_filename)
{
    int syntax = 0;
    char buf[8 * 1024];

    FILE *fp = fopen(proto_filename, "rt");
    if(NULL != fp)
    {
        for(;;)
        {
            if(NULL == fgets(buf, sizeof(buf), fp))
            {
                break;
            }
            else
            {
                int i = 0;
                for(;; ++i)
                {
                    if(' ' != buf[i] && '\t' != buf[i])
                    {
                        break;
                    }
                }

                if(0 == memcmp(buf+i, "syntax", 6))
                {
                    int start = 0;
                    int len = 0;
                    char str_syntax[128];
                    for(;; ++i)
                    {
                        if(isdigit(buf[i]))
                        {
                            start = i;
                            break;
                        }
                    }
                    for(;; ++i)
                    {
                        if(!isdigit(buf[i]))
                        {
                            len = i - start;
                            break;
                        }
                    }
                    memcpy(str_syntax, buf+start, (size_t)len);
                    str_syntax[len] = EOS;
                    syntax = atoi(str_syntax);
                    break;
                }
            }
        }
        fclose(fp);
    }
    return syntax;
}

void convert_pbv3(LPCSTR pbv3_filename, LPCSTR pbv2_filename)
{
    char buf[8 * 1024];
    char append_buf[8*1024];

    append_buf[0] = EOS;
    FILE *fp = fopen(pbv3_filename, "rt");
    if(NULL != fp)
    {
        FILE *fpout = fopen(pbv2_filename, "wt");
        if(NULL!= fpout)
        {
            for(;;)
            {
                if(NULL == fgets(buf, sizeof(buf), fp))
                {
                    break;
                }
                else
                {
                    size_t i = 0;
                    for(;; ++i)
                    {
                        if(' ' != buf[i] && '\t' != buf[i])
                        {
                            break;
                        }
                    }

                    if(0 == memcmp(buf+i, "map", 3))
                    {
                        size_t start = 0;
                        size_t len = 0;
                        char key_type[40];
                        char key_value[256];
                        char field_name[80];
                        char field_no[40];
                        set<string> map_entrys;

                        printf("%s", buf+i);
                        for(i=i+3;; ++i)
                        {
                            if('<'==buf[i])
                            {
                                break;
                            }
                        }
                        start = i + 1;
                        for(;; ++i)
                        {
                            if(','==buf[i] || ' '==buf[i] || '\t'==buf[i])
                            {
                                break;
                            }
                        }
                        len = i - start;
                        // find location of key_type
                        memcpy(key_type, buf + start, len);
                        key_type[len] = EOS;
                        printf("key_type:%s\n", key_type);

                        for(;; ++i)
                        {
                            if(' '!=buf[i] && '\t'!=buf[i] && ','!=buf[i])
                            {
                                break;
                            }
                        }
                        start = i;
                        for(;; ++i)
                        {
                            if('>'==buf[i] || ' '==buf[i] || '\t'==buf[i])
                            {
                                break;
                            }
                        }
                        len = i - start;
                        memcpy(key_value, buf + start, len);
                        key_value[len] = EOS;
                        printf("key_value:%s\n", key_value);

                        for(;; ++i)
                        {
                            if('>'!=buf[i] && ' '!=buf[i] && '\t'!=buf[i])
                            {
                                break;
                            }
                        }
                        start = i;
                        for(;; ++i)
                        {
                            if('='==buf[i] || ' '==buf[i] || '\t'==buf[i])
                            {
                                break;
                            }
                        }
                        len = i - start;
                        memcpy(field_name, buf + start, len);
                        field_name[len] = EOS;
                        printf("field_name:%s\n", field_name);

                        for(;; ++i)
                        {
                            if('='!=buf[i] && ' '!=buf[i] && '\t'!=buf[i])
                            {
                                break;
                            }
                        }
                        start = i;
                        for(;; ++i)
                        {
                            if(';'==buf[i] || ' '==buf[i] || '\t'==buf[i])
                            {
                                break;
                            }
                        }
                        len = i - start;
                        memcpy(field_no, buf + start, len);
                        field_no[len] = EOS;
                        printf("field_no:%s\n", field_no);

                        // use std::set to prevent same name mapentrys
                        string map_entry_name = string("Map") + field_name + "Entry";
                        if(map_entrys.find(map_entry_name) == map_entrys.end())
                        {
                            sprintf(buf, "message Map%sEntry {\n  %s key = 1;\n  %s value = 2;\n}\n\n",
                                    field_name, key_type, key_value);
                            strcat(append_buf, buf);
                            map_entrys.insert(map_entry_name);
                        }
                        sprintf(buf, "repeated Map%sEntry %s = %s;\n", field_name, field_name, field_no);
                    }
                    // if no map keywork, direct copy
                    fputs(buf, fpout);
                }
            }
            fputs(append_buf, fpout);
            printf("----------\n%s----------\n", append_buf);
            fclose(fpout);
        }
        fclose(fp);
    }
}

int main(int argc, char *argv[])
{
    char no_map_filename[256];
    const FileDescriptor *f;
    ImporterError errorCollector;
    compiler::DiskSourceTree sourceTree;
    int retcode = 0;
    sourceTree.MapPath("", ".");

    if (argc < 4)
    {
        printf("Usage: %s NF-Name xxx.proto [xxx.proto] target_dir\n", argv[0]);
        return 1;
    }

    string nf_name = string(argv[1]);
    cout << "nf_name is " << nf_name << endl;

    string target_dir = string(argv[argc-1]);
    if(create_path(target_dir))
    {
        return 2;
    }
    string include_path = target_dir + "include" + path_sep;
    if(create_path(include_path))
    {
        return 2;
    }
    string source_path = target_dir + "source" + path_sep;
    if(create_path(source_path))
    {
        return 2;
    }

    for(int i=2; i<argc-1; ++i)
    {
        compiler::Importer *importer = new compiler::Importer(&sourceTree, &errorCollector);
        proto_filename = argv[i];
        int syntax = get_syntax(proto_filename);
        printf("Proto v%d\n", syntax);

        // if use proto3, convert map keyword.
        ///////////////////////////////////////////////////
        // message MapFieldEntry {
        //   key_type key = 1;
        //   value_type value = 2;
        // }
        // repeated MapFieldEntry Field = N;
        ///////////////////////////////////////////////////
        // map<key_type, value_type> Field = N;
        ///////////////////////////////////////////////////
        if(3 == syntax)
        {
            sprintf(no_map_filename, "%s.tmp_XXXXXX", proto_filename);
#ifdef _WIN32
            strcpy(no_map_filename, _mktemp(no_map_filename));
#else
            strcpy(no_map_filename, mktemp(no_map_filename));
#endif
            FILE *fp = fopen(no_map_filename, "w");
            if(NULL == fp)
            {
                printf("Cannot create tempfile.\n");
                delete importer;
                return 4;
            }
            else
            {
                printf("tempfile %s created.\n", no_map_filename);
                fclose(fp);
            }
            convert_pbv3(proto_filename, no_map_filename);
        }
        else
        {
            strcpy(no_map_filename, proto_filename);
        }

        f = importer->Import(no_map_filename);
        if (NULL == f)
        {
            printf("Cannot import file:%s", no_map_filename);
            retcode = 3;
        }
        else
        {
            retcode = gen_all_from_file(nf_name, f, target_dir);
        }

        if(3 == syntax)
        {
            /* char command[256];
            sprintf(command, "rm -f %s", no_map_filename);
            system(command); */
            remove(no_map_filename);
            printf("tempfile %s removed.\n", no_map_filename);
        }
        delete importer;

        if(0!=retcode)
        {
            return retcode;
        }
    }

    // common header file
    retcode = gen_comm(nf_name, target_dir);
    return retcode;
}
